[DataSource] Connection Pool의 이해와 DataSource
- 커넥션 풀이란?
- 커넥션 풀 구조
- 커넥션 풀 오픈소스
- 커넥션 풀 직접 구현
Connection Pool 이란?
DB와 연결을 짓는 과정자체를 Connection 과정이라고 일컫는다.
여기서 매번 DB와의 연결을 위해서 TCP/IP 3wayhandshake를 과정을 거치기도 하고, 인증과정도 거치게된다.
이런 과정속에서 인증하고 커넥션 작업을 하는데에만 리소스를 많이 사용하게 될 수도 있기에 미리 커넥션을 맺어놓아 보관해두는 공간을 “Connection Pool”이라고 한다.
[Application] --1. 커넥션 요청--> [DB]
[DB] --2. TCP/IP 3-way handshake--> [Application]
[Application] --3. 인증 정보 전달--> [DB]
[DB] --4. 인증 완료, 커넥션 생성--> [Application]
(매 요청마다 위 과정을 반복)- 위의 그림과 같은 과정을 통해 DB의 커넥션을 가지고 올 수 있다
- 단점
- 매번 요청시마다 커넥션을 맺는과정을 거쳐야한다. (리소스를 효율적으로 사용하지 못함)
- DB의 상황에따라 많은 수의 쿼리 요청을 받지 못할 가능성이 존재한다.
- 데이터베이스마다 커넥션 과정의 소요시간은 다르다
- 단점
Connection Pool 구조
[Application] --커넥션 요청--> [Connection Pool]
├─ Connection 1 (사용중)
├─ Connection 2 (대기중)
├─ Connection 3 (대기중)
└─ ... (기본 10개)
│
[Connection Pool] --반환(release)--> 재사용
│
[DB]- Connection Pool이란 쉽게 말해서, 미리 커넥션 과정을 맺어놓고 맺어져 있는 커넥션을 사용하고 반환을 반복하는 공간을 말한다
- 커넥션의 개수의 경우 보통은 10개를 기본으로 설정하고 서버 스펙에 따라 상이하게 설정할 수 있다
- 애플리케이션 로직에서는 커넥션 풀에 있는 커넥션을 조회하고 반환하는 작업을 통해 빠르게 DB작업을 이행할 수 있다
Connection Pool 오픈소스
- Commons-dbcp2
- Tomcat-jdbc pool
- HikariCP (현재 주로 사용됨)
…
- DataSource 인터페이스를 통해서, 오픈소스를 이용하여 Connection Pool을 이용할 수 있음
- OCP, DI를 지키며 구현이 가능
interface DataSource { getConnection(); }
▲ ▲ ▲
│ │ │ (구현)
HikariDataSource Tomcat-jdbc Commons-dbcp2애플리케이션 코드는 구체적인 구현체가 아닌 DataSource 인터페이스에만 의존하므로, 실제 사용하는 커넥션 풀 라이브러리를 다른 것으로 교체하더라도(HikariCP → Tomcat-jdbc 등) 비즈니스 로직 변경 없이 대체할 수 있다.
//test
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(ConnectionConst.URL);
dataSource.setUsername(ConnectionConst.USERNAME);
dataSource.setPassword(ConnectionConst.PASSWORD);
repository = new MemberRepositoryV1(dataSource);
//repositoryV1 Class
private final DataSource dataSource;
public MemberRepositoryV1(DataSource dataSource){
this.dataSource = dataSource;
}Connection Pool 직접 구현
- 생성자를 통해, 커넥션 생성 ( intialSize를 통해 첫 커넥션 수 지정 )
- maxSize를 통해, 커넥션 생성 제한
문제점
여러 스레드가 동시에 요청하면 getConnection 메서드에서 최대 개수 이상으로 커넥션이 생성될 수 있다
(동시성 제어를 위한 lock이 필요)
- DataSource 인터페이스를 직접 구현해도 내부 로직을 통해 getConnection이 호출되는 구조라 한계가 있다
- 실제 사용 시에는 안정성과 성능 면에서 HikariCP 같은 오픈소스 커넥션 풀을 사용하는 것이 권장된다
public class ConnectionPoolEX {
private BlockingQueue<Connection> connectionPool;
private int maxPoolSize;
private String jdbcURL;
private String jdbcUser;
private String jdbcPasswd;
public ConnectionPoolEX(int initialSize,int maxPoolSize, String jdbcURL, String jdbcUser, String jdbcPasswd) throws SQLException{
this.connectionPool = new LinkedBlockingQueue<>();
this.maxPoolSize = maxPoolSize;
this.jdbcURL = jdbcURL;
this.jdbcUser = jdbcUser;
this.jdbcPasswd = jdbcPasswd;
for (int i = 0; i<initialSize; i++){
connectionPool.add(createConnection());
}
}
private Connection createConnection() throws SQLException {
return DriverManager.getConnection(jdbcURL,jdbcUser,jdbcPasswd);
}
public Connection getConnection() throws InterruptedException, SQLException{
Connection connection = connectionPool.poll();
if (connection == null){
if(connectionPool.size() < maxPoolSize){
connection = createConnection();
}else{
connection = connectionPool.take(); // 요소가 사용가능할때까지 대기
}
}
return connection;
}
public void releaseConnection(Connection connection){
if(connection != null){
connectionPool.offer(connection); // 꽉차자지 않았을경우, 삽입 (가득찬 경우 지정된 시간까지 대기)
}
}
public void shutdown() throws SQLException{
for (Connection connection : connectionPool) {
connection.close();
}
}
}